#pragma once

typedef struct ObjectInfo
{
    GUID* guid;
    char* strPath;
    char* progid;
}OBJINFO, *LPOBJINFO;

#define STR(x) #x
#define STR2(y) STR(\x##y)

#define DEFGUID(g1,g2,g3,g4,g5,g6,g7,g8,g9,g10,g11,g12,g13,g14,g15,g16) \
    STR2(g4) STR2(g3) STR2(g2) STR2(g1) STR2(g6) STR2(g5) STR2(g8) STR2(g7) STR2(g9) STR2(g10) STR2(g11) STR2(g12) STR2(g13) STR2(g14) STR2(g15) STR2(g16)

#define DEFITEM(iid, clsid, filename, interface) \
    {(GUID*)iid clsid, filename, interface},


template<typename dst_type, typename src_type>
dst_type pointer_cast(src_type src)
{
    return *static_cast<dst_type*>( static_cast<void*>(&src) );
}

template<typename Pointer>
int PointerToUINT(Pointer pointer)
{
    return pointer_cast<unsigned int>(pointer);
}

#define SINK_BEGIN(SinkName)                                                            \
template<class CHost, class ISink>                                                      \
class SinkName : public CComObjectRootEx<CComMultiThreadModel>                          \
    , public ISink                                                                      \
{                                                                                       \
    BEGIN_COM_MAP(SinkName)                                                             \
        COM_INTERFACE_ENTRY(ISink)                                                      \
    END_COM_MAP()                                                                       \
public:                                                                                 \
    SinkName(CHost* pHost)                                                              \
    {                                                                                   \
        memset((BYTE*)&m_pHost, 0, sizeof(*this) - ((BYTE*)&m_pHost - (BYTE*)this));    \
        m_pHost = pHost;                                                                \
    }                                                                                   \
    virtual ~SinkName()                                                                 \
    {}                                                                                  \
    BOOL UnhookAll()                                                                    \
    {                                                                                   \
        BYTE* start = (BYTE*)(&m_pHost + 1);                                            \
        BYTE* end = (BYTE*)this + sizeof(*this);                                        \
        BOOL bHasActiveHook = FALSE;                                                    \
        for (BYTE* p = start; p != end; ++p)                                            \
        {                                                                               \
            if (*p) bHasActiveHook = TRUE;                                              \
            *p = 0;                                                                     \
        }                                                                               \
        return bHasActiveHook;                                                          \
    }                                                                                   \
public:                                                                                 \
    CHost*  m_pHost;

#define SINK_FUNC(FuncName, ParamDeclare, CallArg)                                      \
    typedef HRESULT (CHost::*F##FuncName)ParamDeclare;                                  \
                                                                                        \
    void Hook_##FuncName(F##FuncName pf##FuncName)                                      \
    {                                                                                   \
        m_pf##FuncName = pf##FuncName;                                                  \
    }                                                                                   \
                                                                                        \
    void Unhook_##FuncName()                                                            \
    {                                                                                   \
        m_pf##FuncName = NULL;                                                          \
    }                                                                                   \
                                                                                        \
    virtual HRESULT __stdcall FuncName ParamDeclare                                     \
    {                                                                                   \
        if (m_pHost && m_pf##FuncName)                                                  \
        {                                                                               \
            return ((m_pHost)->*(m_pf##FuncName))CallArg;                               \
        }                                                                               \
        return E_FAIL;                                                                  \
    }                                                                                   \
                                                                                        \
private:                                                                                \
    F##FuncName m_pf##FuncName;

#define SINK_END()                                                                      \
};

#define SINK_PTR(SinkName, ISink)                                                       \
template<class CHost>                                                                   \
class SinkName##Ptr : public CComPtr< SinkName<CHost, ISink> >                          \
{                                                                                       \
    typedef SinkName<CHost, ISink> SinkType;                                            \
                                                                                        \
public:                                                                                 \
    SinkName##Ptr(CHost* pHost)                                                         \
    {                                                                                   \
        CComObject< SinkName<CHost, ISink> > * pObj = NULL;                             \
        CComObject< SinkName<CHost, ISink> >::CreateInstance(&pObj);                    \
        if (pObj)                                                                       \
        {                                                                               \
            p = pObj;                                                                   \
            p->AddRef();                                                                \
            p->m_pHost = pHost;                                                         \
        }                                                                               \
    }                                                                                   \
                                                                                        \
    SinkType* operator = (__in_opt SinkType* lp) throw()                                \
    {                                                                                   \
        if (p && *this != lp)                                                           \
        {                                                                               \
            p->m_pHost = NULL;                                                          \
            if (p->UnhookAll())                                                         \
            {                                                                           \
                OnAssignBeforeUnhook();                                                 \
            }                                                                           \
        }                                                                               \
        return CComPtr<SinkType>::operator = (lp);                                      \
    }                                                                                   \
                                                                                        \
    virtual ~SinkName##Ptr()                                                            \
    {                                                                                   \
        if (p)                                                                          \
        {                                                                               \
            p->m_pHost = NULL;                                                          \
            if (p->UnhookAll())                                                         \
            {                                                                           \
                OnImplicitUnhook();                                                     \
            }                                                                           \
        }                                                                               \
    }                                                                                   \
                                                                                        \
private:                                                                                \
    void OnAssignBeforeUnhook()                                                         \
    {                                                                                   \
        /* log : assign new value before unhook */                                      \
    }                                                                                   \
                                                                                        \
    void OnImplicitUnhook()                                                             \
    {                                                                                   \
        /* log : not unhook util the obj destruct */                                    \
    }                                                                                   \
};\

